package com.zhang.common.core.utils.jwt;

import com.zhang.common.core.constant.TokenConstants;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;

/**
 * @Class com.zhang.common.core.utils
 * @Author zhang
 * @Date 2025/9/15 16:20
 * @Description Jwt工具类
 **/
public class JwtUtils {

    private static final Logger log = LoggerFactory.getLogger(JwtUtils.class);

    // Jwts.SIG.HS512.key().build() - 自动生成安全的512位密钥
    private final SecretKey secretKey;
    private final String issuer;
    private final long accessExpire;
    private final long refreshExpire;
    private final JwtParser parser;


    /**
     * 构造函数初始化
     *
     * @param secret 至少64字符的密钥字符串
     */
    public JwtUtils(String secret) {
        this(secret, TokenConstants.DEFAULT_ISSUER, TokenConstants.DEFAULT_ACCESS_EXPIRE, TokenConstants.DEFAULT_REFRESH_EXPIRE);
    }

    /**
     * 自定义配置初始化
     *
     * @param secret 至少64字符的密钥字符串
     * @param issuer 指定配置信息
     * @param accessExpire 过期时间
     * @param refreshExpire 刷新时间
     */
    public JwtUtils(String secret, String issuer, long accessExpire, long refreshExpire) {
        this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
        this.issuer = issuer;
        this.accessExpire = accessExpire;
        this.refreshExpire = refreshExpire;
        this.parser = Jwts.parser()
                .verifyWith(secretKey)
                .build();
    }

    /**
     * 生成访问令牌 (带默认声明)
     *
     * @param subject 用户标识 (通常是用户名或用户ID)
     * @return JWT 字符串
     */
    public String generateAccessToken(String subject) {
        return generateToken(subject, Map.of(), accessExpire);
    }

    /**
     * 生成带自定义声明的访问令牌
     *
     * @param subject 用户标识
     * @param claims 自定义声明
     * @return JWT 字符串
     */
    public String generateAccessToken(String subject, Map<String, Object> claims) {
        return generateToken(subject, claims, accessExpire);
    }

    /**
     * 生成刷新令牌
     *
     * @param subject 用户标识
     * @return JWT 字符串
     */
    public String generateRefreshToken(String subject) {
        return Jwts.builder()
                .issuer(issuer)
                .subject(subject)
                .issuedAt(Date.from(Instant.now()))
                .expiration(Date.from(Instant.now().plusSeconds(refreshExpire)))
                .id(UUID.randomUUID().toString())
                .signWith(secretKey, Jwts.SIG.HS512) // 刷新令牌使用更强算法
                .compact();
    }

    /**
     * 生成令牌核心方法
     *
     * @param subject 用户标识
     * @param claims 自定义声明
     * @param expiration 过期时间
     * @return JWT 字符串
     */
    private String generateToken(String subject, Map<String, Object> claims, long expiration) {
        JwtBuilder builder = Jwts.builder()
                .issuer(issuer)
                .subject(subject)
                .issuedAt(Date.from(Instant.now()))
                .expiration(Date.from(Instant.now().plusSeconds(expiration)))
                .id(UUID.randomUUID().toString())
                .signWith(secretKey, Jwts.SIG.HS256);

        // 自定义声明判空
        if (claims != null && !claims.isEmpty()) {
            builder.claims(claims);
        }

        return builder.compact();
    }

    /**
     * 解析令牌获取声明
     *
     * @param token JWT 字符串
     * @return Claims 对象
     * @throws JwtException 如果令牌无效
     */
    public Claims parseToken(String token) throws JwtException {
        return parser.parseSignedClaims(token).getPayload();
    }

    /**
     * 安全解析令牌 (带异常处理)
     *
     * @param token JWT 字符串
     * @return Claims 对象或null(如果无效)
     */
    public Claims safeParse(String token) {
        try {
            return parseToken(token);
        } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException ex) {
            log.warn("解析令牌异常: {}", ex.getMessage(), ex);
        }
        return null;
    }

    /**
     * 验证令牌是否有效
     * @param token JWT 字符串
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        return safeParse(token) != null;
    }

    /**
     * 验证令牌是否有效且属于指定用户
     * @param token JWT 字符串
     * @param subject 预期用户标识
     * @return 是否有效且匹配
     */
    public boolean validateToken(String token, String subject) {
        Claims claims = safeParse(token);
        return claims != null &&
                claims.getSubject().equals(subject) &&
                !isTokenExpired(claims);
    }

    /**
     * 检查令牌是否过期
     * @param claims 已解析的声明
     * @return 是否过期
     */
    public boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }

    /**
     * 刷新访问令牌 (保留原始声明)
     * @param token 原始令牌
     * @return 新令牌
     * @throws JwtException 如果原始令牌无效
     */
    public String refreshToken(String token) throws JwtException {
        Claims claims = parseToken(token);
        return generateToken(claims.getSubject(), claims, accessExpire);
    }

    /**
     * 从令牌中提取特定声明
     * @param token JWT 字符串
     * @param claimResolver 声明提取函数
     * @return 声明值
     * @param <T> 返回值类型
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimResolver) {
        final Claims claims = parseToken(token);
        return claimResolver.apply(claims);
    }

    /**
     * 从令牌中提取用户名
     * @param token JWT 字符串
     * @return 用户名/用户ID
     */
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 从令牌中提取过期时间
     * @param token JWT 字符串
     * @return 过期时间
     */
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }
}
